package com.wlf.admin.common.config;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.dao.SaTokenDao;

import java.util.Map;

/**
 * 功能描述: 异步惰性清理过期token
 *
 * @author wlf 2023/10/23 11:18
 */
public class CleanToken {
    /**
     * 功能描述: 启动方法
     *
     * @author wlf 2023/10/23 11:22
     */
    public static void init() {
        new CleanToken().initRefreshThread();
    }

    /**
     * 存储数据的集合
     */
    public Map<String, Object> dataMap = JdbcMap.getMap(JdbcMap.MapName.data);

    /**
     * 存储数据过期时间的集合（单位: 毫秒）, 记录所有 key 的到期时间 （注意存储的是到期时间，不是剩余存活时间）
     */
    public Map<String, Long> expireMap = JdbcMap.getMap(JdbcMap.MapName.expire);

    /**
     * 如果指定的 key 已经过期，则立即清除它
     *
     * @param key 指定 key
     */
    void clearKeyByTimeout(String key) {
        Long expirationTime = expireMap.get(key);
        // 清除条件：
        // 		1、数据存在。
        // 		2、不是 [ 永不过期 ]。
        // 		3、已经超过过期时间。
        if (expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) {
            dataMap.remove(key);
            expireMap.remove(key);
        }
    }

    /**
     * 执行数据清理的线程引用
     */
    public Thread refreshThread;

    /**
     * 是否继续执行数据清理的线程标记
     */
    public volatile boolean refreshFlag;

    /**
     * 清理所有已经过期的 key
     */
    public void refreshDataMap() {
        for (String s : expireMap.keySet()) {
            clearKeyByTimeout(s);
        }
    }

    /**
     * 初始化定时任务，定时清理过期数据
     */
    public void initRefreshThread() {
        // 如果开发者配置了 <=0 的值，则不启动定时清理
        if (SaManager.getConfig().getDataRefreshPeriod() <= 0) {
            return;
        }

        // 启动定时刷新
        this.refreshFlag = true;
        this.refreshThread = new Thread(() -> {
            for (; ; ) {
                try {
                    try {
                        // 如果已经被标记为结束
                        if (!refreshFlag) {
                            return;
                        }
                        // 执行清理
                        refreshDataMap();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    // 休眠N秒
                    int dataRefreshPeriod = SaManager.getConfig().getDataRefreshPeriod();
                    if (dataRefreshPeriod <= 0) {
                        dataRefreshPeriod = 1;
                    }
                    Thread.sleep(dataRefreshPeriod * 1000L);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        this.refreshThread.start();
    }

    /**
     * 结束定时任务，不再定时清理过期数据
     */
    public void endRefreshThread() {
        this.refreshFlag = false;
    }
}
